#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/icmp6.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <net/if.h>

int create_icmp_if()
{
	int fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
	if (fd < 0) {
		perror("socket");
		exit(0);
	}

	struct icmp6_filter filters;
	ICMP6_FILTER_SETBLOCKALL(&filters);
	ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filters);
	ICMP6_FILTER_SETPASS(ICMP6_ECHO_REQUEST, &filters);
	ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filters);
	ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filters);
	if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filters, sizeof(filters)) < 0) {
		perror("setsockopt");
		exit(0);
	}

	return fd;
}

void analysis_echo(struct icmp6_hdr *icmp)
{
	if (icmp->icmp6_type == ICMP6_ECHO_REPLY) {
		printf("echo reply id=%d seq=%d\n", icmp->icmp6_id, icmp->icmp6_seq);
	} else {
		printf("echo request id=%d seq=%d\n", icmp->icmp6_id, icmp->icmp6_seq);
	}
}

void analysis_rs_solicit(struct nd_router_solicit *rs)
{
	if(rs->nd_rs_type == ND_ROUTER_SOLICIT){
		printf("route solicit ");
	}
}

void analysis_icmp(struct icmp6_hdr *icmp)
{
	switch (icmp->icmp6_type) {
	case ICMP6_ECHO_REPLY:
	case ICMP6_ECHO_REQUEST:
		analysis_echo(icmp);
		break;
	case ND_ROUTER_ADVERT:
		break;
	case ND_ROUTER_SOLICIT:
		analysis_rs_solicit((void *)icmp);
		break;
	default:
		printf("unknow type %d\n", icmp->icmp6_type);
		break;
	}
}

int main()
{
	char buf[1024], iobuf[1024], control[1024], hname[INET6_ADDRSTRLEN];
	struct iovec vec = { iobuf, sizeof(iobuf) };
	struct sockaddr_storage addr;
	struct msghdr msg = { &addr, sizeof(addr), &vec, 1, control, sizeof(control), 0 };

	int fd = create_icmp_if();
	int len = 0, ret = 0;
	for (;;) {
		if ((len = recvmsg(fd, &msg, 0)) < 0) {
			perror("recvmsg");
			exit(0);
		}
		if ((ret = getnameinfo((void *)msg.msg_name, msg.msg_namelen, hname, sizeof(hname), NULL, 0, NI_NUMERICHOST)) < 0) {
			perror(gai_strerror(ret));
			exit(0);
		}
		printf("recv len=%d from=%s\n", len, hname);
		analysis_icmp(msg.msg_iov[0].iov_base);
	}
	return 0;
}